﻿#pragma warning disable CS1591
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using static System.Linq.Expressions.Expression;

namespace Common
{
    /// <summary>
    /// 对象映射
    /// </summary>
    [Obsolete("这个对象映射扩展不完善，无法处理复杂对象，请勿使用")]
    public class Mapper//<TSource, TTarget> where TSource : class where TTarget : class, new()
    {
        //public readonly static Func<TSource, TTarget> MapFunc = GetMapFunc();

        //public readonly static Action<TSource, TTarget> MapAction = GetMapAction();

        //private static MapperConfig<TSource, TTarget> _config { get; set; } = new MapperConfig<TSource, TTarget>();        

        public static void CreatConfig<TSource, TTarget>(Action<MapperConfig<TSource, TTarget>> action) where TSource : class where TTarget : class, new()
        {
            var config = new MapperConfig<TSource, TTarget>();
            action.Invoke(config);
            MapperHelper<TSource, TTarget>.Config = config;
        }

        /// <summary>
        /// 将对象TSource转换为TTarget
        /// </summary>
        /// <param name="source"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TTarget Map<TSource, TTarget>(TSource source, Action<TSource, TTarget> action = null) where TSource : class where TTarget : class, new()
        {
            if (source == null) return null;

            var target = MapperHelper<TSource, TTarget>.MapFunc(source);
            MapperHelper<TSource, TTarget>.Config?.BindAction?.Invoke(source, target);
            action?.Invoke(source, target);
            return target;
        }

        public static void Map<TSource, TTarget>(TSource source, TTarget target, Action<TSource, TTarget> action) where TSource : class where TTarget : class, new()
        {
            if (source == null || target == null) return;

            MapperHelper<TSource, TTarget>.MapAction(source, target);
            MapperHelper<TSource, TTarget>.Config?.BindAction?.Invoke(source, target);
            action?.Invoke(source, target);
        }
        public static void Map<TSource, TTarget>(TSource source, TTarget target) where TSource : class where TTarget : class, new()
        {
            Map(source, target, null);
        }

        public static List<TTarget> MapList<TSource, TTarget>(IEnumerable<TSource> sources, Action<TSource, TTarget> action = null) where TSource : class where TTarget : class, new()
        {
            if (sources == null) return null;

            var list = new List<TTarget>();
            foreach (var item in sources)
            {
                list.Add(Map(item, action));
            }
            return list;
        }


        internal static Func<TSource, TTarget> GetMapFunc<TSource, TTarget>() where TSource : class where TTarget : class, new()
        {
            return source =>
            {
                var target = new TTarget();
                MapperHelper<TSource, TTarget>.MapAction(source, target);
                return target;
            };
        }

        internal static Action<TSource, TTarget> GetMapAction<TSource, TTarget>() where TSource : class where TTarget : class, new()
        {
            MapperHelper<TSource, TTarget>.Config = new MapperConfig<TSource, TTarget>();

            var sourceType = typeof(TSource);
            var targetType = typeof(TTarget);

            if (IsEnumerable(sourceType) || IsEnumerable(targetType))
                throw new NotSupportedException("Enumerable types are not supported,please use MapList method.");

            //Func委托传入变量
            var sourceParameter = Parameter(sourceType, "p");

            var targetParameter = Parameter(targetType, "t");

            //创建一个表达式集合
            var expressions = new List<Expression>();

            // x.GetIndexParameters().Length ==0  过滤 this[index]  索引项
            var targetTypes = targetType.GetProperties().Where(x => x.GetIndexParameters().Length == 0 && (x.PropertyType.IsPublic || x.PropertyType.IsNestedPublic) && x.CanWrite);

            //过滤忽略项
            if (MapperHelper<TSource, TTarget>.Config.IgnoreColoums != null && MapperHelper<TSource, TTarget>.Config.IgnoreColoums.Count > 0)
            {
                targetTypes = targetTypes.Where(x => !MapperHelper<TSource, TTarget>.Config.IgnoreColoums.Contains(x.Name));
            }

            var sourceTypes = sourceType.GetProperties().Where(x => x.GetIndexParameters().Length == 0 && (x.PropertyType.IsPublic || x.PropertyType.IsNestedPublic) && x.CanRead);
            foreach (var targetItem in targetTypes)
            {
                var sourceItem = sourceTypes.FirstOrDefault(x => string.Compare(x.Name, targetItem.Name, MapperHelper<TSource, TTarget>.Config.IgnoreCase) == 0);

                //判断实体的读写权限
                if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
                    continue;

                //标注NotMapped特性的属性忽略转换
                if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
                    continue;

                var sourceProperty = Property(sourceParameter, sourceItem);
                var targetProperty = Property(targetParameter, targetItem);


                //当非值类型且类型不相同时
                if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType
                    && sourceItem.PropertyType != typeof(string) && targetItem.PropertyType != typeof(string)
                    )
                {
                    //判断都是(非泛型、非数组)class
                    if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass
                        && !sourceItem.PropertyType.IsArray && !targetItem.PropertyType.IsArray
                        && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
                    {
                        var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                        expressions.Add(Assign(targetProperty, expression));
                    }

                    //集合数组类型的转换
                    if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
                    {
                        var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                        expressions.Add(Assign(targetProperty, expression));
                    }

                    continue;
                }

                //可空类型转换到非可空类型，当可空类型值为null时，用默认值赋给目标属性；不为null就直接转换
                if (IsNullableType(sourceItem.PropertyType) && !IsNullableType(targetItem.PropertyType))
                {
                    var hasValueExpression = Equal(Property(sourceProperty, "HasValue"), Constant(true));
                    var conditionItem = Condition(hasValueExpression, Convert(sourceProperty, targetItem.PropertyType), Default(targetItem.PropertyType));
                    expressions.Add(Assign(targetProperty, conditionItem));
                    continue;
                }

                //非可空类型转换到可空类型，直接转换
                if (!IsNullableType(sourceItem.PropertyType) && IsNullableType(targetItem.PropertyType))
                {
                    var memberExpression = Convert(sourceProperty, targetItem.PropertyType);
                    expressions.Add(Assign(targetProperty, memberExpression));
                    continue;
                }
                //源属性和目标属性是枚举和int时
                if ((targetItem.PropertyType.IsEnum && sourceItem.PropertyType == typeof(int)) || (targetItem.PropertyType == typeof(int)) && sourceItem.PropertyType.IsEnum)
                {
                    var convertExpr = Convert(sourceProperty, targetItem.PropertyType);
                    expressions.Add(Assign(targetProperty, convertExpr));
                    continue;
                }
                //如果目标类型是可空枚举，源类型是可空int
                if ((IsNullableEnum(targetItem.PropertyType) && IsNullableInt(sourceItem.PropertyType)) || (IsNullableEnum(sourceItem.PropertyType) && IsNullableInt(targetItem.PropertyType)))
                {
                    var hasValExpr = Equal(Property(sourceProperty, "HasValue"), Constant(true));
                    var condExpr = Condition(hasValExpr, Convert(sourceProperty, targetItem.PropertyType), Default(targetItem.PropertyType));
                    expressions.Add(Assign(targetProperty, condExpr));
                }
                if (targetItem.PropertyType != sourceItem.PropertyType)
                    continue;


                expressions.Add(Assign(targetProperty, sourceProperty));

            }

            //当Target!=null判断source是否为空
            var testSource = NotEqual(sourceParameter, Constant(null, sourceType));
            var ifTrueSource = Block(expressions);
            var conditionSource = IfThen(testSource, ifTrueSource);

            //判断target是否为空
            var testTarget = NotEqual(targetParameter, Constant(null, targetType));
            var conditionTarget = IfThen(testTarget, conditionSource);

            var lambda = Lambda<Action<TSource, TTarget>>(conditionTarget, sourceParameter, targetParameter);
            return lambda.Compile();
        }

        private static Expression GetClassExpression(Expression sourceProperty, Type sourceType, Type targetType)
        {
            //条件p.Item!=null
            var testItem = NotEqual(sourceProperty, Constant(null, sourceType));

            //构造回调 Mapper<TSource, TTarget>.Map()
            var mapperType = typeof(Mapper);//.MakeGenericType(sourceType, targetType);
            //var mapMethod = mapperType.GetMethod(nameof(Map), new Type[] { sourceType, targetType }).MakeGenericMethod(sourceType, targetType);
            var mapMethod = mapperType.GetMethods().First(m => m.Name.Equals(nameof(Map)) && m.IsGenericMethod && m.GetGenericArguments().Length == 2)
                .MakeGenericMethod(sourceType, targetType);

            var actionType = typeof(Action<,>).MakeGenericType(sourceType, targetType);

            var iftrue = Call(mapMethod, sourceProperty, Constant(null, actionType));

            var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));

            return conditionItem;
        }

        private static Expression GetListExpression(Expression sourceProperty, Type sourceType, Type targetType)
        {
            //条件p.Item!=null
            var testItem = NotEqual(sourceProperty, Constant(null, sourceType));

            //构造回调 Mapper<TSource, TTarget>.MapList()
            var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0];
            var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0];
            var mapperType = typeof(Mapper); //.MakeGenericType(sourceArg, targetArg);

            var actionType = typeof(Action<,>).MakeGenericType(sourceArg, targetArg);
            var mapMethod = mapperType.GetMethod(nameof(Map), new Type[] { sourceType, actionType }).MakeGenericMethod(sourceType, targetType);
            var mapperExecMap = Call(mapMethod, sourceProperty, Constant(null, actionType));

            Expression iftrue;
            if (targetType == mapperExecMap.Type)
            {
                iftrue = mapperExecMap;
            }
            else if (targetType.IsArray)//数组类型调用ToArray()方法
            {
                iftrue = Call(typeof(Enumerable), nameof(Enumerable.ToArray), new[] { mapperExecMap.Type.GenericTypeArguments[0] }, mapperExecMap);
            }
            else if (typeof(IDictionary).IsAssignableFrom(targetType))
            {
                iftrue = Constant(null, targetType);//字典类型不转换
            }
            else
            {
                iftrue = Convert(mapperExecMap, targetType);
            }

            var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));

            return conditionItem;
        }

        private static bool IsNullableType(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        private static bool IsEnumerable(Type type)
        {
            return type.IsArray
                   || type.GetInterfaces().Any(x => x == typeof(ICollection) || x == typeof(IEnumerable));
        }
        public static bool IsNullableEnum(Type type)
        {
            return IsNullableType(type) && type.GetGenericArguments().First().IsEnum;
        }
        public static bool IsNullableInt(Type type)
        {
            return IsNullableType(type) && type.GetGenericArguments().First() == typeof(int);
        }
    }

    internal class MapperHelper<TSource, TTarget> where TSource : class where TTarget : class, new()
    {
        private static Func<TSource, TTarget> _mapFunc;

        [Obsolete]
        public static Func<TSource, TTarget> MapFunc
        {
            get
            {
                if (_mapFunc == null)
                {
                    _mapFunc = Mapper.GetMapFunc<TSource, TTarget>();
                }
                return _mapFunc;
            }
            set
            {
                _mapFunc = value;
            }
        }
        private static Action<TSource, TTarget> _mapAction;

        [Obsolete]
        public static Action<TSource, TTarget> MapAction
        {
            get
            {
                if (_mapAction == null)
                {
                    _mapAction = Mapper.GetMapAction<TSource, TTarget>();
                }
                return _mapAction;
            }
            set
            {
                _mapAction = value;
            }
        }

        [Obsolete]
        public static MapperConfig<TSource, TTarget> Config { get; set; } = new MapperConfig<TSource, TTarget>();
    }
}
#pragma warning restore CS1591